// GroupListBox.cpp : implementation file
//

#include "stdafx.h"
#include "GroupListBox.h"
#include "boost\foreach.hpp"


// CGroupListBox

IMPLEMENT_DYNAMIC(CGroupListBox, CListBox)

CGroupListBox::CGroupListBox()
{
  background_brush_.CreateSolidBrush(::GetSysColor(COLOR_3DSHADOW));
  selected_brush_.CreateSolidBrush(::GetSysColor(COLOR_HIGHLIGHT));

  small_font_base_size_       = 60;
  medium_font_base_size_      = 100;
  large_font_base_size_       = 120;
  super_font_base_size_       = 180;
  small_font_relative_size_   = 100;
  medium_font_relative_size_  = 100;
  large_font_relative_size_   = 100;
  super_font_relative_size_   = 100;
  internal_margin_            = 3;
  external_margin_            = 3;
  left_margin_                = 3;
  headerHeight                = 0;
  valueHeight                 = 0;
  useHeight                   = 0;
  indicatorHeight             = 0;
  indicatorWidth              = 0;

  int pointSize = small_font_base_size_*small_font_relative_size_/100;
  small_font_.CreatePointFont(pointSize, _T("Arial"));

  pointSize = medium_font_base_size_*medium_font_relative_size_/100;
  medium_font_.CreatePointFont(pointSize, _T("Tahoma"));

  pointSize = large_font_base_size_*large_font_relative_size_/100;
  large_font_.CreatePointFont(pointSize, _T("Tahoma"));

  pointSize = super_font_base_size_*super_font_relative_size_/100;
  super_font_.CreatePointFont(pointSize, _T("Tahoma"));
}

CGroupListBox::~CGroupListBox()
{
}

int CGroupListBox::AddValueItem(const CValueItem& ValueItem)
{
  int cmdStart = -1;
  int cmdEnd   = -1;
  int numItems = static_cast<int>(value_items_.size());

  for (int n=0; n<numItems; ++n) {
    if (ValueItem.cmd_name() == value_items_[n].cmd_name()) {
      cmdStart = n;
      break;
    }
  }

  if (cmdStart != -1) {
    for (int n=cmdStart+1; n<numItems; ++n) {
      if (ValueItem.cmd_name() != value_items_[n].cmd_name()) {
        cmdEnd = n;
        break;
      }
    }
  }

  return InsertValueItem(cmdEnd, ValueItem);
}

int CGroupListBox::DeleteValueItem(int Index)
{
  int numItems = static_cast<int>(value_items_.size());
  if (Index<0 || Index>numItems-1) {
    return LB_ERR;
  }

  int nIndex = CListBox::DeleteString(Index);
  if (nIndex == LB_ERR) {
    ASSERT(value_items_.size()==GetCount());
    return LB_ERR;
  }

  ASSERT(value_items_.size()==GetCount());
  return nIndex;
}

int CGroupListBox::InsertValueItem(int Index, const CValueItem& ValueItem)
{
  int numItems = static_cast<int>(value_items_.size());
  if (Index<-1 || Index>numItems) {
    return LB_ERR;
  }

  int nIndex = CListBox::InsertString(Index, _T(""));
  if (nIndex==LB_ERR || nIndex==LB_ERRSPACE) {
    ASSERT(value_items_.size()==GetCount());
    return nIndex;
  }

  if (Index == -1) {
    value_items_.push_back(ValueItem);
  } else {
    value_items_.insert(value_items_.cbegin()+Index, ValueItem);
  }

  ASSERT(value_items_.size()==GetCount());
  return nIndex;
}

int CGroupListBox::FindValueItem(const tstring& Name, const tstring& Cmd) const
{
  int nIndex = 0;
  BOOST_FOREACH(const CValueItem& valueItem, value_items_)
  {
    if (valueItem.Name()==Name && valueItem.cmd_name()==Cmd) {
      return nIndex;
    }

    ++nIndex;
  }

  return -1;
}

int CGroupListBox::SetValueItem(int Index, const tstring& Value, bool Ok)
{
  int numItems = static_cast<int>(value_items_.size());
  if (Index<0 || Index>numItems-1) {
    return LB_ERR;
  }

  tstring tValue = Value;
  tstring::size_type firstComma = Value.find(_T(','), 0);
  if (firstComma!=tstring::npos) {
    tstring::size_type secondComma = Value.find(_T(','), firstComma+1);
    if (secondComma!=tstring::npos) {
      tstring::size_type lastComma = Value.rfind(_T(','));
      if (secondComma<lastComma) {
        tValue.replace(secondComma+1, lastComma-secondComma-1, 3, '.');
      }
    }
  }

  value_items_[Index].set_measure_value(tValue);
  value_items_[Index].set_ok(Ok);
  SetCurSel(Index);

  return Index;
}

int CGroupListBox::SetItemName(int Index, const tstring& newName)
{
  int numItems = static_cast<int>(value_items_.size());
  if (Index<0 || Index>numItems-1) {
    return LB_ERR;
  }

  value_items_[Index].set_name(newName);
  SetCurSel(Index);

  return Index;
}


BEGIN_MESSAGE_MAP(CGroupListBox, CListBox)
  ON_WM_CTLCOLOR_REFLECT()
  ON_WM_CREATE()
END_MESSAGE_MAP()



// CGroupListBox message handlers




BOOL CGroupListBox::PreCreateWindow(CREATESTRUCT& cs)
{
  if (!CListBox::PreCreateWindow(cs)) {
    return FALSE;
  }

  cs.style &= ~(LBS_OWNERDRAWVARIABLE | LBS_SORT | LBS_HASSTRINGS);
  cs.style |= LBS_OWNERDRAWFIXED;

  return TRUE;
}


void CGroupListBox::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
  ASSERT(value_items_.size()==GetCount());
  lpMeasureItemStruct->itemHeight = 80;
}


void CGroupListBox::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
  if (lpDrawItemStruct->itemID == -1) {
    return;
  }

  ASSERT(value_items_.size()==GetCount());
  CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
  ENSURE(pDC);

  int saveID = pDC->SaveDC();
  pDC->SetBkMode(TRANSPARENT);

  int   nItem  = lpDrawItemStruct->itemID;
  CRect rcDisp = lpDrawItemStruct->rcItem;
  CRect rcFull = lpDrawItemStruct->rcItem;
  rcDisp.DeflateRect(left_margin_, external_margin_, external_margin_, 0);

  CRect rcBack = lpDrawItemStruct->rcItem;
  rcBack.InflateRect(0, 0, 0, external_margin_);
  if (lpDrawItemStruct->itemState&ODS_SELECTED) {
    pDC->FillRect(rcBack, &selected_brush_);
  } else {
    pDC->FillRect(rcBack, &background_brush_);
  }

  int cyInternal = (useHeight-indicatorHeight)/2;
  CRect rcIndicator(CPoint(rcFull.right - external_margin_ - 3*internal_margin_ -
    indicatorWidth, rcFull.top + external_margin_ + cyInternal),
    CSize(indicatorWidth + 2*internal_margin_, indicatorHeight));

  int headerLeft = rcFull.left + left_margin_ + internal_margin_;
  int headerTop  = rcFull.top + external_margin_;
  CRect rcHeader(CPoint(headerLeft, headerTop), CSize(rcIndicator.left -
    internal_margin_ - headerLeft, headerHeight));

  cyInternal = (useHeight-headerHeight-valueHeight)/2;
  CRect rcValue(CPoint(rcHeader.left, rcHeader.bottom + cyInternal),
    CSize(rcIndicator.left - internal_margin_ - headerLeft, valueHeight));

  pDC->FillSolidRect(rcDisp, RGB(0, 0, 0));

  pDC->SelectObject(&medium_font_);
  pDC->SetTextColor(RGB(153, 204, 255));
  pDC->DrawText(value_items_[nItem].Name().c_str(), rcHeader,
    DT_SINGLELINE|DT_LEFT|DT_VCENTER);

  pDC->SetTextColor(value_items_[nItem].ok()?RGB(0x00, 0xFF, 0x00):RGB(0xFF, 0x00, 0x00));
  pDC->SelectObject(&large_font_);
  pDC->DrawText(value_items_[nItem].measure_value().c_str(), rcValue,
    DT_SINGLELINE|DT_CENTER|DT_VCENTER);

  pDC->SelectObject(&super_font_);
  CBrush bkBrush(RGB(64, 64, 64));
  pDC->FillRect(rcIndicator, &bkBrush);
  if (value_items_[nItem].ok()) {
    pDC->DrawText(CString("OK"), rcIndicator, DT_SINGLELINE|DT_CENTER|DT_VCENTER);
  } else {
    pDC->DrawText(CString("NG"), rcIndicator, DT_SINGLELINE|DT_CENTER|DT_VCENTER);
  }

  pDC->RestoreDC(saveID);
}


void CGroupListBox::DeleteItem(LPDELETEITEMSTRUCT lpDeleteItemStruct)
{
  value_items_.erase(value_items_.cbegin()+lpDeleteItemStruct->itemID);
  CListBox::DeleteItem(lpDeleteItemStruct);
}


HBRUSH CGroupListBox::CtlColor(CDC* /*pDC*/, UINT /*nCtlColor*/)
{
  return background_brush_;
}


void CGroupListBox::PreSubclassWindow()
{
  DWORD dwStyle = GetStyle();

  ASSERT(dwStyle&LBS_OWNERDRAWFIXED);
  ASSERT(!(dwStyle&LBS_OWNERDRAWVARIABLE));
  ASSERT(!(dwStyle&LBS_SORT));
  ASSERT(!(dwStyle&LBS_HASSTRINGS));

  CClientDC dc(this);

  TEXTMETRIC tm;
  CFont* fontOld = dc.SelectObject(&medium_font_);
  dc.GetTextMetrics(&tm);
  headerHeight = tm.tmHeight;

  dc.SelectObject(&large_font_);
  dc.GetTextMetrics(&tm);
  valueHeight = tm.tmHeight;

  dc.SelectObject(&super_font_);
  dc.GetTextMetrics(&tm);
  indicatorHeight = tm.tmHeight;

  CSize okSize = dc.GetTextExtent(_T("OK"));
  CSize ngSize = dc.GetTextExtent(_T("NG"));
  indicatorWidth = max(okSize.cx, ngSize.cx);

  useHeight = max((headerHeight+valueHeight), indicatorHeight+2*internal_margin_);
  SetItemHeight(0, external_margin_+useHeight);
  dc.SelectObject(fontOld);

  CListBox::PreSubclassWindow();
}


int CGroupListBox::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
  if (CListBox::OnCreate(lpCreateStruct) == -1)
    return -1;

  return 0;
}

void CGroupListBox::Reset(void)
{
  for (int n=0; n<GetCount(); ++n) {
    SetValueItem(n, _T("0"), false);
  }
}

BOOL CGroupListBox::AllOk( void ) const
{
  BOOST_FOREACH(const CValueItem& valueItem, value_items_)
  {
    if (valueItem.ok()==false) {
      return FALSE;
    }
  }

  return TRUE;
}
